#ifndef __GLF_TASK_TASKDIRECTOR_DECL_H_INCLUDED__
#define __GLF_TASK_TASKDIRECTOR_DECL_H_INCLUDED__

#include <glf/core/threadTraits.h>
#include <glf/core/thread.h>
#include <list>
#include <map>

namespace glf {

class TaskHandlerBase;
class TaskThreadImpl;

struct TaskThreadData 
{
	void* ptr;
	int coreAffinityMask;
};

class TaskDirector
{
private:
	// This maximum number is per group.
	static const int MAX_WORKER_THREAD = 16;

	struct ThreadList
	{
		ThreadList()
			: m_Count(0)
		{
		}

		TaskThreadImpl* m_Thread[MAX_WORKER_THREAD];
		int m_Count;
	};

public:

	~TaskDirector();

	template<typename Tag1>
	void StartThread(int n, void* data = NULL);

	template<typename Tag1, typename Tag2>
	void StartThread(int n, void* data = NULL);

	template<typename ThreadType>
	void StartThread(ThreadList& tl, int n, void* data);

	template<typename Tag1>
	void StartThread(int n, TaskThreadData& data);

	template<typename Tag1, typename Tag2>
	void StartThread(int n, TaskThreadData& data);

	template<typename ThreadType>
	void StartThread(ThreadList& tl, int n, TaskThreadData& data);

	template<typename Tag1>
	void StopThread();
	
	template<typename Tag1, typename Tag2>
	void StopThread();

	template<typename Tag1>
	int GetThreadCount();

	template<typename Tag1, typename Tag2>
	int GetThreadCount();

	void StopThread(ThreadList& tl);

	static TaskDirector& GetInstance();

	// IsDone return true if there is no task pending and there is no task 
	// currently executing for a certain tage
	template<typename Tag>
	bool IsDone();
	
	template<typename Tag1, typename Tag2>
	bool IsDone();

	// CleanUp return true if there is no more thread to delete
	bool CleanUp();

	void RemoveUnused();

	//
	// About Registered Handler
	//
	// A handler can register itself to one or more threads. Once that handler is
	// registered, you can call ConsumeRegisteredHandler to consume task on all 
	// registered for this thread.
	//
	// This is used for example in Glitch for the graphics tasks which can only
	// be executed on the main thread. Cpu Tasks can push graphics tasks and wait 
	// for them. If a DrainTask<CPU_TASK> is called on the main thread, the 
	// graphic task handler will be starved and the system may deadlock.
	//
	// DrainTask will call ConsumeRegisteredHandler, so if it is called on the
	// man thread, it will make sure all handler associated with this thread
	// are also called (but they won't be necessarily be drained) to prevent
	// any deadlock.
	//
	// In a very dynamic environment, this system could also be used to add/remove
	// handler in a worker thread. However, using the registration system in this
	// manner may requires small API adjustment.
	//

	void RegisterHandler(TaskHandlerBase* th, ThreadId id);
	void UnregisterHandler(TaskHandlerBase* th, ThreadId id);
	bool IsEquivalentHandlerRegistered(TaskHandlerBase* th);

	bool ConsumeRegisteredHandler();

	template<typename Tag>
	void ForceImmediateTaskExecution(bool executeImmediately);
	
	template<typename Tag>
	void PauseTasksConsumption();
	
	template<typename Tag>
	void ResumeTasksConsumption();
	
private:

	TaskDirector();

	template<typename Tag1>
	ThreadList& GetList();

	template<typename Tag1, typename Tag2>
	ThreadList& GetList();

	typedef std::list<TaskThreadImpl*> DeleteList;
	DeleteList m_DeleteList;

	template<typename Tag1, typename Tag2>
	class Holder
	{
	public:

		static ThreadList& GetList();

	private:

		static ThreadList* s_ThreadListInstance;
		static int s_Lock;
	};

	typedef std::list<TaskHandlerBase*> TaskHandlerList;
	typedef std::map<ThreadId, TaskHandlerList> TaskHandlerMap;
	TaskHandlerMap m_TaskHandlerMap;

	static TaskDirector* s_TaskDirectorInstance;
	static int s_Lock;
};

// I would had prefer to encapsulate this inside the task director, however
// since most of the task code is in header file, this resulted in circular
// #include statement.

template<typename Tag1, typename Tag2>
TaskDirector::ThreadList* TaskDirector::Holder<Tag1, Tag2>::s_ThreadListInstance = NULL;

template<typename Tag1, typename Tag2>
int TaskDirector::Holder<Tag1, Tag2>::s_Lock = 0;

} // end namespace glf

#endif // __GLF_TASK_TASKDIRECTOR_DECL_H_INCLUDED__
